1 using System.Collections;
2 using ThreeDPool.Managers;
3 using ThreeDPool.EventHandlers;
4 using UnityEngine;
5
6 namespace ThreeDPool.Controllers
7 {
8 class CueController : MonoBehaviour
9 {
10 [SerializeField]
11 private Transform _cueBall = null;
12
13 // distance from the cue ball
14 private float _defaultDistFromCueBall;
15
16 private float _maxClampDist = 9;
17
18 // the distance from cue ball to the cue determines the force gathered
19 private float _forceGathered = 0.0f;
20
21 // minimum force threshold required to consider a valid shot
22 private float _forceThreshold = 0.5f;
23
24 private float _speed = 10.0f;
25 private bool _cueReleasedToStrike = false;
26
27 private Vector3 _initialPos;
28 private Vector3 _initialDir;
29
30 // this is the position to rotate around when the ball is been striked to stationary
31 // the default value for this vector should be one to avoid unexpected behavior
32 private Vector3 _posToRot = Vector3.one;
33
34 public float ForceGatheredToHit { get { return _forceGathered; } }
35
36 private void Start()
37 {
38 // cache the initial position and rotation
39 _initialPos = transform.position;
40 _initialDir = transform.forward;
41
42 // making sure the distance is same as what we started with
43 _defaultDistFromCueBall = Vector3.Distance(_cueBall.position, transform.position);
44
45 EventManager.Subscribe(typeof(GameInputEvent).Name, OnGameInputEvent);
46 EventManager.Subscribe(typeof(CueBallActionEvent).Name, OnCueBallEvent);
47 EventManager.Subscribe(typeof(GameStateEvent).Name, OnGameStateEvent);
48 }
49
50 private void OnDestroy()
51 {
52 EventManager.Unsubscribe(typeof(GameInputEvent).Name, OnGameInputEvent);
53 EventManager.Unsubscribe(typeof(CueBallActionEvent).Name, OnCueBallEvent);
54 EventManager.Unsubscribe(typeof(GameStateEvent).Name, OnGameStateEvent);
55 }
56
57 private void OnGameInputEvent(object sender, IGameEvent gameEvent)
58 {
59 GameInputEvent gameInputEvent = (GameInputEvent)gameEvent;
60 // start moving the cue stick towards the ball
61 switch (gameInputEvent.State)
62 {
63 case GameInputEvent.States.HorizontalAxisMovement:
64 {
65 if (_posToRot == Vector3.one)
66 transform.RotateAround(_cueBall.position, Vector3.up, 20f * gameInputEvent.axisOffset * Time.deltaTime);
67 else
68 transform.RotateAround(_posToRot, Vector3.up, 20f * gameInputEvent.axisOffset * Time.deltaTime);
69 }
70 break;
71 case GameInputEvent.States.VerticalAxisMovement:
72 {
73 // this means that the ball is moving
74 if (_posToRot != Vector3.one)
75 return;
76
77 // clamp the cue movement, else it will be frustrating for the player
78 var newPosition = transform.position + transform.forward * gameInputEvent.axisOffset;
79
80 _forceGathered = Vector3.Distance(_cueBall.position, newPosition);
81 if ((_forceGathered < _defaultDistFromCueBall + _maxClampDist) &&
82 _forceGathered > _defaultDistFromCueBall)
83 {
84 transform.position = newPosition;
85 EventManager.Notify(typeof(CueActionEvent).ToString(), this, new CueActionEvent() { ForceGathered = _forceGathered });
86 }
87 else
88 {
89 }
90
91 }
92 break;
93 case GameInputEvent.States.Release:
94 {
95 // the cue ball is not stationary as of now
96 if (_posToRot != Vector3.one)
97 return;
98
99 if (_forceGathered > _defaultDistFromCueBall + _forceThreshold)
100 _cueReleasedToStrike = true;
101 }
102 break;
103 }
104 }
105
106 /// <summary>
107 /// handle cue controller based on the cue ball events
108 /// </summary>
109 /// <param name="sender"></param>
110 /// <param name="gameEvent"></param>
111 private void OnCueBallEvent(object sender, IGameEvent gameEvent)
112 {
113 CueBallActionEvent cueBallActionEvent = (CueBallActionEvent)gameEvent;
114
115 // start moving the cue stick towards the ball
116 switch (cueBallActionEvent.State)
117 {
118 case CueBallActionEvent.States.Stationary:
119 case CueBallActionEvent.States.Default:
120 {
121 // making sure everything is clean
122 _forceGathered = 0f;
123
124 // on ready for next shot position the cue controller closer to cue ball
125 transform.position = _cueBall.transform.position - transform.forward * _defaultDistFromCueBall;
126 transform.LookAt(_cueBall);
127
128 _posToRot = Vector3.one;
129 }
130 break;
131 case CueBallActionEvent.States.Striked:
132 {
133 _cueReleasedToStrike = false;
134
135 // make the cue ball go back in the play state
136 if (GameManager.Instance.CurrGameState == GameManager.GameState.Play)
137 {
138 // move the cue backward after striking so that cue ball doesnt touch the cue
139 StartCoroutine(MoveCueAfterStrike(transform.position, _cueBall.transform.position - transform.forward * _defaultDistFromCueBall * 1.5f, 1.0f));
140 }
141
142 transform.LookAt(_cueBall);
143
144 _posToRot = _cueBall.transform.position;
145 }
146 break;
147 }
148 }
149
150 private void OnGameStateEvent(object sender, IGameEvent gameEvent)
151 {
152 GameStateEvent gameStateEvent = (GameStateEvent)gameEvent;
153 switch(gameStateEvent.GameState)
154 {
155 case GameStateEvent.State.Play:
156 {
157 PlaceInInitialPosAndRot();
158 }
159 break;
160 }
161 }
162
163 /// <summary>
164 /// this function makes the cue move after striking the cueball
165 /// </summary>
166 /// <param name="source"></param>
167 /// <param name="target"></param>
168 /// <param name="overTime"></param>
169 /// <returns></returns>
170 IEnumerator MoveCueAfterStrike(Vector3 source, Vector3 target, float overTime)
171 {
172 float startTime = Time.time;
173 while (Time.time < startTime + overTime)
174 {
175 transform.position = Vector3.Lerp(source, target, (Time.time - startTime) / overTime);
176 yield return null;
177 }
178 transform.position = target;
179 }
180
181 private void FixedUpdate()
182 {
183 if(_cueReleasedToStrike)
184 {
185 float step = _speed * Time.deltaTime * (_forceGathered/_speed);
186 transform.position = Vector3.MoveTowards(transform.position, _cueBall.transform.position, step);
187
188 // cue ball will now detect if the cue actually hit it and then behave accordingly
189 // this event will be notifies by the cue ball
190 }
191 }
192
193 private void PlaceInInitialPosAndRot()
194 {
195 _forceGathered = 0f;
196 _cueReleasedToStrike = false;
197 _posToRot = Vector3.one;
198
199 transform.position = _initialPos;
200 transform.forward = _initialDir;
201 }
202 }
203 }